package com.auto.reg.util;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.net.telnet.TelnetClient;

import com.auto.reg.model.NeteaseProxy;

public class ProxyUtil {
	
	private List<NeteaseProxy> proxyList = new ArrayList<NeteaseProxy>();
	
	public final Integer startSize = 100;
	public final Integer minSize = 80;
	
	private String defaultProxy = "110.4.12.170:80";
	
	public static void main(String[] args) {
		ProxyUtil util = new ProxyUtil();
		util.getAvailableProxyList();
	}
	
	public List<NeteaseProxy> getAvailableProxyList()
	{
		long startTime = System.currentTimeMillis();
		if (!this.proxyList.isEmpty())
		{
			for (int i = 0; i < this.proxyList.size(); i++)
			{
				NeteaseProxy proxy = this.proxyList.get(i);
				if (!this.isTelnetOk(proxy.getHost(), proxy.getPort())
						|| !this.isProxyOk(proxy.getHost(), proxy.getPort()))
				{
					NeteaseProxy removedProxy = this.proxyList.remove(i--);
					this.info("removing proxy : " + removedProxy.getHost() + ", " + removedProxy.getPort());
				}
			}
		}
		
		String proxyStr = this.defaultProxy;;
		
		if (this.proxyList.size() < this.startSize)
		{
			int count = 1;
			List<Integer> pageList = new ArrayList<Integer>();
			for (int i = 1; i <= 10; i++)
			{
				pageList.add(i);
			}
			Collections.shuffle(pageList);
			
			find:
			for (Integer i : pageList)
			{
				String proxyUrl = "http://www.cnproxy.com/proxy" + i + ".html";
				
				String page = this.getPage(proxyUrl, null, "GBK", proxyStr.split(":")[0], Integer.parseInt(proxyStr.split(":")[1]), 30000, 30000, null, null);
				
//				this.info(page);
				
				List<NeteaseProxy> proxyList = this.parseProxyList(page);
				
				for (NeteaseProxy proxy : proxyList)
				{
					this.info("try : " + count++ + "------------------------");
					if (!this.isContains(proxy) && this.isTelnetOk(proxy.getHost(), proxy.getPort()) && this.isProxyOk(proxy.getHost(), proxy.getPort()))
					{
						this.proxyList.add(proxy);
						this.info("*** congratulations, add proxy, current size : " + this.proxyList.size());
						if (this.proxyList.size() >= this.startSize)
						{
							break find;
						}
					}
				}
			}
			if (this.proxyList.size() < this.startSize)
			{
				throw new RuntimeException("can not find enough proxy");
			}
		}
		long endTime = System.currentTimeMillis();
		this.info("getAvailableProxyList cost : " + (endTime - startTime) + "ms");
		return this.proxyList;
	}
	
	private boolean isContains(NeteaseProxy proxy)
	{
		boolean result = false;
		for (NeteaseProxy tempProxy : this.proxyList)
		{
			if (tempProxy.getHost().equals(proxy.getHost()))
			{
				this.info("host already exists : " + proxy.toString());
				result = true;
				break;
			}
		}
		return result;
	}
	
	protected List<NeteaseProxy> parseProxyList(String page)
	{
		List<NeteaseProxy> result = new ArrayList<NeteaseProxy>();
		
		Pattern pattern = Pattern.compile("<SCRIPT type=\"text/javascript\">([^<]+)</SCRIPT>");
		Matcher matcher = pattern.matcher(page);
		Map<String, Integer> numberMap = new HashMap<String, Integer>();
		if (matcher.find())
		{
			String numbers = matcher.group(1);
			String[] numberArr = numbers.split(";");
			for (String number : numberArr)
			{
				String[] parts = number.split("=");
				numberMap.put(parts[0].trim(), Integer.parseInt(parts[1].substring(1, 2)));
			}
			
			System.out.println(numberMap);
			
			int startIndex = page.lastIndexOf("<table>");
			int endIndex = page.lastIndexOf("</table>");
			
			String servers = page.substring(startIndex + 7, endIndex);
			
			Pattern patternTr = Pattern.compile("<tr>(.*?)</tr>");
			Matcher matcherTr = patternTr.matcher(servers);
			Pattern patternTd = Pattern.compile("<td>(.*?)</td>");
			while (matcherTr.find())
			{
				String server = matcherTr.group(1);
				
//				this.info(server);
				
				Matcher matcherTd = patternTd.matcher(server);
				if (matcherTd.find())
				{
					String item = matcherTd.group(1);
					
					if (item.contains("<") && item.contains("+") && item.contains(""))
					{
						String host = item.substring(0, item.indexOf("<"));
						String port = item.substring(item.indexOf("+") + 1, item.indexOf(")"));
						
						String portStr = "";
						String[] portParts = port.split("\\+");
						for (String portPart : portParts)
						{
							portStr += numberMap.get(portPart);
						}
						
//						this.info(host + ", " + portStr);
						
						result.add(new NeteaseProxy(host, Integer.parseInt(portStr)));
					}
				}
			}
		}
		return result;
	}
	
	protected boolean isTelnetOk(String host, Integer port)
	{
		boolean result = false;
		TelnetClient telnet = new TelnetClient(); 
		try
		{
			telnet.connect(host, port);
			result = true;
		}
		catch (Exception e)
		{
			this.info("isTelnetOk error : " + e.getMessage());
		}
		this.info("isTelnetOk : " + host + ", " + port + ", " + result);
		return result;
	}
	
	private HttpClient client = new HttpClient();
	
	public String getPage(String url, String param, String encoding)
	{
		return this.getPage(url, param, encoding, null, null, 30000, 30000, null, null);
	}
	
	public synchronized String getPage(String url, String param, String encoding, 
			String host, Integer port, Integer connTimeout, Integer soTimeout, String ip, Map<String,String> requestHeaderMap)
	{
		this.info("url : " + url + ", " + encoding + ", " + host + ", " + port + ", " + connTimeout + ", " + soTimeout + ", " + ip);
		String result = null;
		try
		{

			GetMethod method = null;
			if (host != null && port != null)
			{
				method = new GetMethod(url);
				HostConfiguration hostConfiguration = new HostConfiguration();
				URI uri = new URI(url,true);
				hostConfiguration.setHost(uri);
				client.setHostConfiguration(hostConfiguration);
				client.getHostConfiguration().setProxy(host, port);
			}
			else
			{
				method = new GetMethod(url);
			}
			client.getHttpConnectionManager().getParams().setConnectionTimeout(connTimeout);
			client.getHttpConnectionManager().getParams().setSoTimeout(soTimeout);
			
			if (requestHeaderMap != null) 
			{
				for (Entry<String,String> entry : requestHeaderMap.entrySet()) 
				{
					method.setRequestHeader(entry.getKey(), entry.getValue());
				}
			}
			else
			{
				client.getParams().setParameter(HttpMethodParams.USER_AGENT, "Mozilla/5.0 (X11; U; Windows XP; zh-CN; rv:1.9.1.2) Gecko/20090803  Firefox/3.5.2");
			}
			if (ip != null) 
			{
				method.addRequestHeader("X-Forwarded-For", ip);
			}
			
			int status = client.executeMethod(method);
			this.info("status : " + status);
			if (status == 200)
			{
				InputStream stream = method.getResponseBodyAsStream();
				if (stream != null)
				{
					ByteArrayOutputStream data = new ByteArrayOutputStream();
					int readCount = 0;
					byte[] buffer = new byte[512];
					while ((readCount = stream.read(buffer)) > 0)
					{
						data.write(buffer, 0, readCount);
					}
					String response = new String(data.toByteArray(), encoding);
//					this.info(response);
					result = response;
				}
			}
			
			method.releaseConnection();
		}
		catch (Exception e)
		{
			this.info("getPage error : " + e.getMessage());
		}
		return result;
	}
	
	protected boolean isProxyOk(String host, Integer port)
	{
		boolean result = false;
		long startTime = 0;
		long endTime = 0;
		try
		{
			String url = "http://www.baidu.com";
			String encoding = "UTF-8";
			startTime = System.currentTimeMillis();
			String response = this.getPage(url, null, encoding, host, port, 15000, 15000, null, null);
			endTime = System.currentTimeMillis();
			result = response != null && response.contains("百度");
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		this.info("isProxyOk : " + host + ":" + port + ", result : " + result + ", time cost : " + (endTime - startTime) + "ms");
		return result;
	}
	
	protected void info(String msg) {
		System.out.println(msg);
//		LoggerUtil.proxyInfo(msg);
	}
}
